#include "defines.h"
#include "global.h"
#include "assert.h"

void xPredIntraLumaDC(unsigned char *pSrc, pel_t *pDst, int i_dst, int iBlkWidth, int iBlkHeight, bool bAboveAvail, bool bLeftAvail)
{
	int x, y, tmp;

	if(!bAboveAvail && !bLeftAvail){
		for(y=0;y<iBlkHeight;y++){
			for(x=0;x<iBlkWidth;x++){
				pDst[x] = 128;
			}
			pDst += i_dst;
		}
	}
	else if(bAboveAvail && !bLeftAvail){
		for(y=0;y<iBlkHeight;y++){
			for(x=0;x<iBlkWidth;x++){
				pDst[x] = (pel_t)((pSrc[x - 1] + 4 * pSrc[x] + 6 * pSrc[1 + x] + 4 * pSrc[2 + x] + pSrc[3 + x] + 8) >> 4);
			}
			pDst += i_dst;
		}
	}
	else if (!bAboveAvail && bLeftAvail){
		for (y = 0; y < iBlkHeight; y++){
			tmp = (pel_t)((pSrc[-y + 1] + 4 * pSrc[-y] + 6 * pSrc[-1 - y] + 4 * pSrc[-2 - y] + pSrc[-3 - y] + 8) >> 4);
			for (x = 0; x < iBlkWidth; x++){
				pDst[x] = tmp;
			}
			pDst += i_dst;
		}
	}
	else if (bAboveAvail && bLeftAvail){
		for (y = 0; y < iBlkHeight; y++){
			if(y == 0){
				tmp = ((pSrc[-y] + 4 * pSrc[-y] + 6 * pSrc[-1 - y] + 4 * pSrc[-2 - y] + pSrc[-3 - y] + 8) >> 4);
			}else{
				tmp = ((pSrc[-y + 1] + 4 * pSrc[-y] + 6 * pSrc[-1 - y] + 4 * pSrc[-2 - y] + pSrc[-3 - y] + 8) >> 4);
			}

			for (x = 0; x < iBlkWidth; x++){
				if(x == 0){
					pDst[x] = (pel_t)((((pSrc[x] + 4 * pSrc[x] + 6 * pSrc[1 + x] + 4 * pSrc[2 + x] + pSrc[3 + x] + 8) >> 4) + tmp) >> 1);
				}
				else{
					pDst[x] = (pel_t)((((pSrc[x - 1] + 4 * pSrc[x] + 6 * pSrc[1 + x] + 4 * pSrc[2 + x] + pSrc[3 + x] + 8) >> 4) + tmp) >> 1);
				}	
			}
			pDst += i_dst;
		}
	}
}

void xPredIntraVer(unsigned char *pSrc, pel_t *pDst, int i_dst, int iBlkWidth, int iBlkHeight)
{
	int x, y;

	for(y=0;y<iBlkHeight;y++){
		for(x=0;x<iBlkWidth;x++){
			pDst[x]=pSrc[x + 1];
		}
		pDst += i_dst;
	}
}

void xPredIntraHor(unsigned char *pSrc, pel_t *pDst, int i_dst, int iBlkWidth, int iBlkHeight)
{
	int x, y;

	for(y=0;y<iBlkHeight;y++){
		for(x=0;x<iBlkWidth;x++){
			pDst[x]=pSrc[-1-y];
		}
		pDst += i_dst;
	}
}

void xPredIntraDownRight(unsigned char *pSrc, pel_t *pDst, int i_dst, int iBlkWidth, int iBlkHeight)
{
	int x, y;

	for(y=0;y<iBlkHeight;y++){
		for(x=0;x<iBlkWidth;x++){
			pDst[x]=pSrc[x-y];
		}
		pDst += i_dst;
	}
}

void xPredIntraDownLeft(unsigned char *pSrc, pel_t *pDst, int i_dst, int iBlkWidth, int iBlkHeight)
{
	int x, y;

	for(y=0;y<iBlkHeight;y++){
		for(x=0;x<iBlkWidth;x++){
			pDst[x]=(pSrc[2+x+y]+pSrc[-2-x-y])>>1;
		}
		pDst += i_dst;
	}
}


void xPredIntraChromaDC(unsigned char *pSrc, pel_t *pDst, int i_dst, int iBlkWidth, int iBlkHeight, int x_off, int y_off, uchar bAboveAvail, uchar bLeftAvail)
{
	int x, y;

	if(!bAboveAvail && !bLeftAvail){
		for(y=0;y<iBlkHeight;y++){
			for(x=0;x<iBlkWidth;x++){
				pDst[x] = 128;
			}
			pDst += i_dst;
		}
	}
	else if(bAboveAvail && !bLeftAvail){
		pSrc = pSrc + x_off;
		for(y=0;y<iBlkHeight;y++){
			for(x=0;x<iBlkWidth;x++){
				pDst[x]=pSrc[1+x];
			}
			pDst += i_dst;
		}
	}
	else if(!bAboveAvail && bLeftAvail){
		pSrc = pSrc - y_off;
		for(y=0;y<iBlkHeight;y++){
			for(x=0;x<iBlkWidth;x++){
				pDst[x] = pSrc[-1-y];
			}
			pDst += i_dst;
		}
	}
	else if(bAboveAvail && bLeftAvail){
		for(y=0;y<iBlkHeight;y++){
			for(x=0;x<iBlkWidth;x++){
				pDst[x]=(pSrc[1+x+x_off]+pSrc[-1-y-y_off])>>1;
			}
			pDst += i_dst;
		}
	}
}

void xPredIntraChromaPlane(unsigned char *pSrc, pel_t *pDst, int i_dst, int iBlkWidth, int iBlkHeight, int x_off, int y_off)
{
	int x, y, i;
	int ih, iv, ib, ic, iaa;
	ih = 0;
	iv = 0;
	for (i=1;i<5;i++)
	{
		ih += i*(pSrc[1+i+3] - pSrc[1-i+3]);
		iv += i*(pSrc[-1-i-3] - pSrc[-1+i-3]);
	}
	ib=(17*ih+16)>>5;
	ic=(17*iv+16)>>5;
	iaa=16*(pSrc[1+7]+pSrc[-1-7]);
	for (y=0; y<iBlkHeight; y++){
		for (x=0; x<iBlkWidth; x++){
			pDst[x]=(pel_t)max(0,min(255,(iaa+(x+x_off-3)*ib +(y+y_off-3)*ic + 16)/32));
		}
		pDst += i_dst;
	}
}


void xIntraPredFilter(unsigned char *pSrc)
{
	int i;
	unsigned char tmp;
	unsigned char last_pix = pSrc[-8];

	for(i=-8; i<=8; i++)
	{
		tmp = (unsigned char)((last_pix + (pSrc[i]<<1) + pSrc[i+1] + 2 )>>2);
		last_pix = pSrc[i];
		pSrc[i] = tmp;
	}
}

void intra_pred_luma(unsigned char *pSrc, pel_t *pDst, int i_dst, int predmode, int uhBlkWidth, bool bAboveAvail, bool bLeftAvail)
{
	switch(predmode){
	case DC_PRED:// 0 DC
		g_funs_handle.intra_pred_luma_dc(pSrc, pDst, i_dst, uhBlkWidth, uhBlkWidth, bAboveAvail, bLeftAvail);
		break;
	case VERT_PRED:// 1 vertical
		assert(bAboveAvail);
		g_funs_handle.intra_pred_ver(pSrc, pDst, i_dst, uhBlkWidth, uhBlkWidth);
		break;
	case HOR_PRED:// 2 horizontal
		assert(bLeftAvail);
		g_funs_handle.intra_pred_hor(pSrc, pDst, i_dst, uhBlkWidth, uhBlkWidth);
		break;
	case DOWN_RIGHT_PRED:// 3 down-right
		assert(bLeftAvail && bAboveAvail);
		g_funs_handle.intra_pred_downright(pSrc, pDst, i_dst, uhBlkWidth, uhBlkWidth);
		break;

	case DOWN_LEFT_PRED:// 4 up-right bidirectional
		assert(bLeftAvail && bAboveAvail);
		g_funs_handle.intra_pred_downleft(pSrc, pDst, i_dst, uhBlkWidth, uhBlkWidth);
		break;
	}
}

void intra_pred_chroma(unsigned char *pSrc, pel_t *pDst, int i_dst, int predmode, int x_off, int y_off, bool bAboveAvail, bool bLeftAvail)
{
	switch (predmode){
	case DC_PRED_8:
		g_funs_handle.intra_pred_chroma_dc(pSrc, pDst, i_dst, 4, 4, x_off, y_off, bAboveAvail, bLeftAvail);
		break;
	case HOR_PRED_8:
		assert(bLeftAvail);
		g_funs_handle.intra_pred_hor(pSrc - y_off, pDst, i_dst, 4, 4);
		break;
	case VERT_PRED_8:
		assert(bAboveAvail); 
		g_funs_handle.intra_pred_ver(pSrc + x_off, pDst, i_dst, 4, 4);
		break;
	case PLANE_8:
		assert(bLeftAvail && bAboveAvail);
		g_funs_handle.intra_pred_chroma_plane(pSrc, pDst, i_dst, 4, 4, x_off, y_off);
		break;
	default:
		assert(0);
		break;
	}
}

void com_funs_init_intra_pred()
{
	g_funs_handle.intra_pred_luma_dc   = xPredIntraLumaDC;
	g_funs_handle.intra_pred_ver       = xPredIntraVer;
	g_funs_handle.intra_pred_hor       = xPredIntraHor;
	g_funs_handle.intra_pred_downright = xPredIntraDownRight;
	g_funs_handle.intra_pred_downleft  = xPredIntraDownLeft;

	g_funs_handle.intra_pred_chroma_dc    = xPredIntraChromaDC;
	g_funs_handle.intra_pred_chroma_plane = xPredIntraChromaPlane;

	//g_funs_handle.intra_pred_filter = xIntraPredFilter;
}